﻿Imports System.Net.Sockets
Imports System.Threading
Imports NLog

Public Class cSroTcpC
    Inherits cConnectionBase

    Protected pSocket As Socket
    Protected WithEvents socketEvent1 As SocketAsyncEventArgs

    Protected pFisch As New BlowfishNET.BlowfishECB()
    Protected pSecurutyByteGen As New cSecurityByteGen
    Protected pHandShake As cHandShake
    Protected pBuffer(&H7FFF - 1) As Byte

    Protected Logger As NLog.Logger = LogManager.GetLogger("ToServerLogger")

    Public Overrides Sub Start(ByVal ip As String, ByVal port As UInt32)
        Set_Status(eSocketStatus.Handshake)
        Try
            Threading.Thread.Sleep(500)
            pSocket = New Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.IP)
            Dim ep As New System.Net.IPEndPoint(Net.IPAddress.Parse(ip), port)
            If socketEvent1 IsNot Nothing Then socketEvent1.Dispose()
            socketEvent1 = New SocketAsyncEventArgs
            socketEvent1.RemoteEndPoint = ep
            pSocket.ConnectAsync(socketEvent1)
            Console.SetCursorPosition(0, 16)
            Console.WriteLine("Server --> Connecting to --> {0}:{1}", ip, port)
        Catch ex As Exception
            Set_Status(eSocketStatus.Offline)
            Console.SetCursorPosition(0, 16)
            Console.WriteLine("Server --> Connecting to Faild -->  {0}:{1}", ip, port)
        End Try
    End Sub

    Public Overrides Sub Disconnect()
        If Status <> eSocketStatus.Offline Then
            Set_Status(eSocketStatus.Offline)
            Console.SetCursorPosition(0, 16)
            Console.WriteLine("Server --> Disconnect")
        End If
        Try
            pSocket.Shutdown(SocketShutdown.Both)

        Catch ex As Exception
        Finally
            If pSocket IsNot Nothing Then
                pSocket.Close()
            End If
        End Try
    End Sub

    Public Overrides Sub Send(ByRef packet() As Byte)
        'Logger.Info(BitConverter.ToString(packet))
        Logger.Info(BitConverter.ToString(packet))

        Dim flags As UInt32 = BitConverter.ToUInt16(packet, 4)

        If (flags And &H100) <> 0 Then 'crypted
            packet(1) = packet(1) Or &H80 'set hi bit to 1
            pSecurutyByteGen.addSecurity(packet)
            Dim lenoffset As Byte
            lenoffset = (packet.GetUpperBound(0) + 1 - 2) Mod 8
            If lenoffset <> 0 Then
                lenoffset = 8 - lenoffset
                ReDim Preserve packet(packet.GetUpperBound(0) + lenoffset)
            End If

            pFisch.EncryptRev(packet, 2, packet, 2, packet.GetUpperBound(0) + 1 - 2)
        Else
            pSecurutyByteGen.addSecurity(packet)
        End If
        Logger.Info(BitConverter.ToString(packet))
        Try
            pSocket.Send(packet)
        Catch ex As Exception
            Console.SetCursorPosition(0, 16)
            Console.WriteLine("Server send error " & ex.Message.ToString)
        End Try
    End Sub

    Private Sub socketEvent1_Completed(ByVal sender As Object, ByVal e As System.Net.Sockets.SocketAsyncEventArgs) Handles socketEvent1.Completed
        If e.SocketError = SocketError.Success Then

            Select Case e.LastOperation
                Case SocketAsyncOperation.Connect
                    Console.SetCursorPosition(0, 16)
                    Console.WriteLine("Server --> Connected")
                    socketEvent1.SetBuffer(pBuffer, 0, pBuffer.GetUpperBound(0) + 1)
                    pSocket.ReceiveAsync(socketEvent1)
                Case SocketAsyncOperation.Receive
                    ProcessReceive(sender, e)
                Case Else
                    Console.WriteLine(e.LastOperation.ToString)
            End Select

        Else
            Console.SetCursorPosition(0, 16)
            Console.WriteLine("Server --> {0}{1}", e.LastOperation.ToString, e.SocketError.ToString)
            Disconnect()
        End If

    End Sub

    Private Sub ProcessReceive(ByVal sender As Object, ByVal e As System.Net.Sockets.SocketAsyncEventArgs)
        If e.BytesTransferred = 0 Then 'check if connection is lost
            Disconnect()
            Exit Sub
        End If

        Dim len As Int32 = -1
        Dim byteCounter As Int32 = e.BytesTransferred + e.Offset

        Using r As New IO.BinaryReader(New IO.MemoryStream(e.Buffer, 0, byteCounter))

            Do
                Dim PacketStartPos As UInt32 = r.BaseStream.Position

                Dim crypted As Boolean = False
                len = -1
                Dim lenoffset As Byte = 0

                If r.BaseStream.Position + 2 <= byteCounter Then 'try to get packet len 
                    len = r.ReadUInt16()
                    If len >> 15 = 1 Then       'check if crypted
                        crypted = True
                        len = len And &H7FFF    'remove HI Bit from len 
                        'check if we need lenoffset to fit blowfish requirement(divisible by 8)
                        lenoffset = (len + 4) Mod 8
                        If lenoffset <> 0 Then lenoffset = 8 - lenoffset
                    End If
                End If

                If r.BaseStream.Position + 4 + len + lenoffset <= byteCounter Then 'check if packet complett
                    r.BaseStream.Position = PacketStartPos
                    Dim packet As Byte() = r.ReadBytes(len + lenoffset + 6)
                    If crypted = True Then
                        pFisch.DecryptRev(packet, 2, packet, 2, len + 4 + lenoffset)
                        If lenoffset <> 0 Then
                            ReDim Preserve packet(len + 6 - 1)
                        End If
                        packet(4) = 1
                        packet(5) = 1
                        packet(0) = len And &HFF
                        packet(1) = len >> 8
                    Else
                        packet(4) = 1
                        packet(5) = 0
                    End If
                    'Logger.Info("  from server: " & BitConverter.ToString(packet))

                    If Status = eSocketStatus.Handshake Then 'Hanshake not precessed so it has to be a handshake packet ;)
                        If BitConverter.ToInt16(packet, 2) <> &H5000 Then
                            Console.WriteLine("")
                        End If
                        If packet.GetUpperBound(0) + 1 = 37 + 6 Then 'handle the first Hanshake packet
                            pHandShake = New cHandShake
                            Dim ret As cHandShake.sRetVal
                            ret = pHandShake.Packet_0x5000_len37(packet)
                            pSecurutyByteGen.Init(ret.SeedCounter, ret.SeedCrc)
                            Send(ret.packet)
                        ElseIf packet.GetUpperBound(0) + 1 = 9 + 6 Then 'handle the second Hanshake packet
                            Dim key() As Byte = pHandShake.Packet_0x5000_len9(packet)
                            pFisch.Initialize(key, 0, 8)
                            Logger.Info("final key: " & BitConverter.ToString(key))
                            pHandShake.Dispose()
                            Console.SetCursorPosition(0, 16)
                            Console.WriteLine("Server --> HandShake Done")
                            Set_Status(eSocketStatus.Connected)
                        Else
                            Throw New Exception("somthing went horrible wrong at Handshake process ")
                        End If
                    Else 'handshake done normal packet
                        Add_Packet(packet)
                    End If
                Else
                    len = -1
                End If

                If len = -1 Then 'no complett packet --> write it back to buffer
                    r.BaseStream.Position = PacketStartPos
                    Using w As New IO.BinaryWriter(New IO.MemoryStream(pBuffer))
                        w.Write(r.ReadBytes(r.BaseStream.Length - PacketStartPos))
                        w.Flush()
                        socketEvent1.SetBuffer(pBuffer, r.BaseStream.Length - PacketStartPos, pBuffer.GetUpperBound(0) - (r.BaseStream.Length - PacketStartPos) + 1)
                    End Using
                End If

            Loop Until r.BaseStream.Position = r.BaseStream.Length 'done with the data ?
        End Using


        If e.Offset <> 0 Then
            If len <> -1 Then
                socketEvent1.SetBuffer(0, pBuffer.GetUpperBound(0) + 1)
            End If
        End If

        Try
            pSocket.ReceiveAsync(socketEvent1)
        Catch ex As Exception
            Disconnect()
        End Try
    End Sub
End Class
